(import
 (except (rnrs base)
         let-values
         map)
 (only (guile)
       lambda* λ
       call-with-values
       string->list
       string-contains
       char-upper-case?)
 (fileio)
 ;; receive form
 (srfi srfi-8)
 (ice-9 pretty-print))


(define split-off-n
  (λ (lst n)
    (let iter ([lst° lst]
               [counter 0]
               [cont
                ;; The base case of the continuation is
                ;; merely returning the split off part and
                ;; the remaining list as 2 values.
                (λ (acc-split-off rem-lst)
                  (values acc-split-off rem-lst))])
      (cond
       [(null? lst°)
        ;; If the list is completely consumed early, then
        ;; end the list using the empty list and return the
        ;; empty list as remaining list. The empty list
        ;; value needs to be "threaded through the built up
        ;; continuations.
        (cont '() lst°)]
       [(= counter n)
        (cont '() lst°)]
       [else
        (iter (cdr lst°)
              (+ counter 1)
              ;; Make a new continuation which calls the
              ;; base or built up continuation with the
              ;; current split off element and the later
              ;; determined end of the split off
              ;; elements.

              ;; When this continuation is later built upon,
              ;; the argument new-tail will be put after the
              ;; current element, which is the correct order
              ;; of elements.

              ;; When this continuation is later called from
              ;; the base case of recursion, it will receive
              ;; the empty list to finish off the list.

              ;; Basically we are already writing down the
              ;; cons-ing, which will happen, when the base
              ;; case of recursion is reached and the
              ;; continuation is called.
              (λ (new-tail rem-lst)
                (cont (cons (car lst°) new-tail)
                      rem-lst)))]))))


(define split-into-n-sized-segments
  (λ (lst n)
    (cond
     [(null? lst) '()]
     [else
      (receive (split-off-part remaining-list)
          (split-off-n lst n)
        (cons split-off-part
              (split-into-n-sized-segments remaining-list n)))])))


(define char->string
  (λ (char)
    (list->string (list char))))


(define find-common-chars
  (λ (str1 str2)
    (call-with-output-string
      (λ (port)
        (let iter ([chars° (string->list str1)])
          (unless (null? chars°)
            (let ([char-str (char->string (car chars°))])
              (when (string-contains str2 char-str)
                (display char-str port))
              (iter (cdr chars°)))))))))


(define score
  (λ (char)
    (if (char-upper-case? char)
        (- (char->integer char) 38)
        (- (char->integer char) 96))))


(define first-char
  (λ (str)
    (car (string->list (substring str 0 1)))))


(let* ([lines (get-lines-from-file "input")]
       [lines-grouped (split-into-n-sized-segments lines 3)])
  ;; (pretty-print lines-grouped)
  (pretty-print
   (apply +
          (map (λ (group)
                 (score
                  (first-char
                   (reduce find-common-chars "" group))))
               lines-grouped))))
